<!--
 * @Author       : Hejh(3010733382@qq.com)
 * @Version      : V1.0
 * @Date         : 2024-04-10 10:38:29
 * @Description  : 文件上传panel
-->
<template>
  <n-tabs
    class="upload-panel"
    type="line"
    animated
    :value="activeName"
    @update:value="handleActiveNameChange"
  >
    <n-tab-pane name="local-upload" tab="本地上传" display-directive="show">
      <div class="flex justify-between">
        <n-button-group size="small">
          <n-button type="primary" @click="handleSelectFile">
            选择文件
          </n-button>
          <input
            type="file"
            multiple
            ref="fileInput"
            style="display: none"
            @change="handleFileChange"
          />
          <n-dropdown
            trigger="click"
            :options="uploadAction"
            placement="bottom-start"
            @select="handleUploadSelect"
            :z-index="1000"
          >
            <n-button type="tertiary">
              <SvgIcon icon="xiala1" size="13px"></SvgIcon>
            </n-button>
          </n-dropdown>
        </n-button-group>

        <n-button-group size="small">
          <n-button type="tertiary" @click="handleFullTask">
            <SvgIcon :icon="fullStart ? 'zanting' : 'kaishi'"></SvgIcon>
            {{ fullStart ? '暂停' : '开始' }}
          </n-button>

          <n-button type="tertiary" @click="handleCleanAll"> 清空所有</n-button>
          <n-button type="tertiary" @click="handleCleanSuc">
            清空已完成
          </n-button>
        </n-button-group>
      </div>

      <div class="upload-list mt-2">
        <div class="item" v-for="item in uploadList" :key="item.id">
          <div class="inner-pro z-20 flex items-center justify-between">
            <div class="flex items-center ml-1">
              <SvgIcon
                :icon="getFileTypeInfoByType(item.type).myIcon"
              ></SvgIcon>
              <div class="ml-1 text-primaryColor font-bold">
                <n-ellipsis style="max-width: 160px">
                  {{ item.filename }}
                </n-ellipsis>
              </div>
            </div>

            <div class="flex items-center mr-1">
              <span
                class="mr-2 text-[13px] text-[#555]"
                v-if="item.percent !== '100'"
              >
                {{ item.percent }}%
              </span>
              <div class="size mr-2 text-[13px] text-primaryColor">
                {{ convertFromBytes(item.size) }}
              </div>

              <div class="actions flex items-center">
                <n-button
                  class="pause h-[22px] mr-1"
                  @click="handleSingerPause(item)"
                  v-if="item.percent !== '100'"
                >
                  <SvgIcon
                    icon="kaishi"
                    size="16px"
                    v-if="item.isPause"
                  ></SvgIcon>
                  <SvgIcon icon="zanting" size="16px" v-else></SvgIcon>
                </n-button>
                <div
                  v-if="item.percent !== '100'"
                  @click="handleCancelUpload(item)"
                  class="close cursor-pointer flex justify-center items-center mr-1"
                >
                  <i
                    class="iconfont icon-cuowuguanbiquxiao-yuankuang cursor-pointer close-icon"
                  ></i>
                </div>
                <div class="success cursor-pointer mr-1" v-else>
                  <SvgIcon icon="chenggong" size="22px"></SvgIcon>
                </div>
              </div>
            </div>
          </div>

          <div class="inner-pro">
            <n-progress
              :height="35"
              type="line"
              :percentage="item.percent === '100' ? 0 : Number(item.percent)"
              :indicator-placement="'inside'"
              processing
              :border-radius="4"
              color="#aaddff"
              rail-color="#f0f0f0"
              :fill-border-radius="0"
            >
              {{ '' }}
            </n-progress>
          </div>
        </div>
      </div>

      <div
        class="upload-con border min-h-[400px] mt-2 flex justify-center items-center text-[20px] text-[#aaa] rounded-sm"
        @dragover="fileDragover"
        @drop="fileDrop"
      >
        DROP FILES HERE
      </div>
    </n-tab-pane>

    <!-- <n-tab-pane name="offline-download" tab="离线下载" display-directive="show">
    </n-tab-pane> -->
  </n-tabs>
</template>

<script setup lang="ts">
import MyWorker from '@/utils/worker.ts?worker'
import useExplorerStore from '@/stores/modules/explorer'
import { fileChunkUpload, fileHandShark } from '@/api/file'
import type { IndexObj } from '@/types/commom'
import type { UploadTask } from '@/types/file'
import { convertFromBytes } from '@/utils'
import {
  getFileTypeBySuffix,
  getFileTypeInfoByType
} from '@/utils/commonBusiness'

import { events } from '@/utils/bus'

const explorerStore = useExplorerStore()
const activeName = ref('local-upload')

const handleActiveNameChange = (value: string) => {
  activeName.value = value
}

const emits = defineEmits<{
  (e: 'close'): void
}>()

const uploadAction = ref([
  {
    label: '文件夹上传',
    key: 'folder.upload'
  }
])

onBeforeMount(() => {
  events.off('upload-file')
})

onMounted(() => {
  events.on('upload-file', (res: any) => {
    uploadFiles(res.files)
  })
})

// 整体开始暂停开关
const fullStart = ref(false)

const handleFullTask = () => {
  fullStart.value = !fullStart.value
  if (!fullStart.value) {
    // 全部暂停
    uploadList.value.forEach((item) => {
      item.isPause = true
    })
  } else {
    // 全部开始
    uploadList.value.forEach((item) => {
      item.isPause = false
    })
  }
}

onMounted(() => {
  uploadList.value = explorerStore.uploadTasks
})

onUnmounted(() => {
  explorerStore.setUploadTaskList(uploadList.value)
})

// 上传进度的task列表
const uploadList = ref<UploadTask[]>([])

// 清空所有任务
const handleCleanAll = () => {
  uploadList.value = []
}

// 清空已完成任务
const handleCleanSuc = () => {
  uploadList.value = uploadList.value.filter((item) => {
    return +item.percent !== 100
  })
}

// 处理单个任务的暂停，开始
const handleSingerPause = (item: IndexObj) => {
  item.isPause = !item.isPause
  if (!item.isPause) {
    // 继续分片上传
    fileHandShark({
      fileId: item.fileId,
      filename: item.filename,
      size: '' + item.size,
      chunkIds: item.fileInfo.chunks.map((it: any) => it.id),
      parentId: item.parentId
    })
      .then((res) => {
        const needs = res.data.needs
        // if (!needs.length) {
        //   // uploadList.value.find((v) => v.id === item.id).percent = '100'
        //   item.percent = '100'
        // }
        // 说明需要继续上传
        uploadPiece(needs, item)
      })
      .catch((e) => {
        item.isPause = true
        console.error(e)
      })
  }
}

// 取消单个任务
const handleCancelUpload = (item: IndexObj) => {
  item.isPause = true
  // 从task中删除
  uploadList.value = uploadList.value.filter((v) => {
    return v.id !== item.id
  })
}

const handleUploadSelect = (key: string) => {}

const fileInput = ref()
const handleSelectFile = () => {
  const input = fileInput.value
  if (input) {
    input.click()
  }
}
//  点击上传
const handleFileChange = (event: any) => {
  // console.log(event.target!.files)
  uploadFiles(event.target.files)
}

// 拖拽上传
const fileDragover = (e: DragEvent) => {
  console.log(e)
  e.preventDefault()
}

const fileDrop = (e: DragEvent) => {
  e.preventDefault()
  if (!e.dataTransfer) {
    return
  }
  const files = e.dataTransfer.files // 获取到所有的文件对象
  uploadFiles(files)
}

const uploadFiles = async (files: FileList) => {
  for (let i = 0; i < files.length; i++) {
    const random = Math.random().toString(16)
    const fileTask2 = {
      id: 'xxx' + random,
      fileId: 'xxx' + random,
      parentId: '-1',
      filename: '文件计算中...',
      percent: 0,
      size: '0',
      type: 1,
      isPause: false,
      random,
      fileInfo: {
        fileId: 'xxx' + random,
        size: '0',
        filename: 'xxx' + random,
        chunks: []
      },
      needs: []
    }
    uploadList.value.push(fileTask2)
    const myWorker = new MyWorker()
    myWorker.postMessage({ file: files[i] })
    myWorker.onmessage = (e) => {
      handleCutSuccess(e.data)
      myWorker.terminate()
    }

    const handleCutSuccess = (fileInfo: any) => {
      uploadList.value = uploadList.value.filter(
        (item) => item.random !== random
      )
      fileInfo.random = random
      const fileTask = {
        id: fileInfo.fileId + random,
        fileId: fileInfo.fileId,
        parentId: explorerStore.curNav!.key,
        filename: fileInfo.filename,
        percent: 0,
        size: fileInfo.size,
        type: getFileTypeBySuffix(fileInfo.filename).type,
        isPause: false,
        random,
        fileInfo,
        needs: []
      }
      uploadList.value.push(fileTask)
      fileHandShark({
        fileId: fileInfo.fileId,
        filename: fileInfo.filename,
        size: '' + fileInfo.size,
        chunkIds: fileInfo.chunks.map((it: any) => it.id),
        parentId: explorerStore.curNav!.key
      })
        .then((res) => {
          const needs = res.data.needs
          // if (!needs.length) {
          //   fileTask.percent = '100'
          // return
          // }
          // 说明需要继续上传
          uploadPiece(needs, fileTask)
        })
        .catch((e) => {
          // 文件协商失败
          fileTask.isPause = true
          console.error(e)
        })
    }
  }
}

// 上传分片
const uploadPiece = async (needs: string[], fileTask: any) => {
  fileTask.needs = needs
  if (fileTask.isPause) {
    // 记录当前分片
    return
  }

  const curTask = uploadList.value.find((item) => item.id === fileTask.id)

  if (!curTask) {
    return
  }

  if (!needs || needs.length === 0) {
    curTask.percent = '100'
    const isUpload = uploadList.value.every((item) => +item.percent === 100)
    if (isUpload) {
      emits('close')
    }
    return
  }
  const nextChunkId = needs[0]
  const file = fileTask.fileInfo.chunks.find(
    (it: any) => it.id === nextChunkId
  ).content
  const formData = new FormData()
  formData.append('file', file)
  formData.append('chunkId', nextChunkId)
  formData.append('fileId', fileTask.fileInfo.fileId)

  // 上传分片可能失败,这里可以限制上传次数
  let isSucCnt = 0
  while (isSucCnt < 5) {
    await fileChunkUpload(formData)
      .then((res) => {
        isSucCnt = 10
        needs = res.data.needs
        const allLength = fileTask.fileInfo.chunks.length
        curTask!.percent =
          Math.floor(((allLength - needs.length) / allLength) * 100) + ''
        uploadPiece(needs, fileTask)
      })
      .catch(() => {
        isSucCnt++
        // uploadPiece(needs, fileTask)
        console.log('error')
      })
  }

  if (isSucCnt === 5) {
    // 说明该分片上传失败，可以暂停该上传，提示上传失败
    fileTask.isPause = true
    console.error(fileTask.filename + '分片上传失败')
  }
}
</script>

<style scoped lang="scss">
.upload-panel {
  .upload-list {
    .item {
      display: flex;
      align-items: center;
      height: 35px;
      min-height: 35px;
      background: #fafdff;
      position: relative;
      margin-bottom: 5px;

      .close-icon {
        font-size: 22px;
        color: #ccc;
        &:hover {
          color: #ff4d4f;
        }
      }

      .inner-pro {
        left: 0;
        top: 0;
        bottom: 0;
        right: 0;
        position: absolute;
      }
    }
  }
}
</style>
